package com.polaris.live.im.manager

import androidx.lifecycle.LifecycleOwner
import com.polaris.live.common.manager.CoroutineScopeManager
import com.polaris.live.common.util.StructUtils
import com.polaris.live.common.util.computeIfAbsentBy21
import com.polaris.live.common.util.ifThen
import com.polaris.live.common.util.removeIfBy24
import com.polaris.live.im.sfs.LIVE_INVITE_FOLLOW
import com.polaris.live.im.sfs.LIVE_INVITE_GIFT
import com.polaris.live.im.sfs.LIVE_NEW_FOLLOWER
import com.polaris.live.im.sfs.LIVE_PROMPT
import com.polaris.live.im.sfs.LIVE_PUBLIC_SCREEN
import com.polaris.live.im.sfs.LIVE_RECOMMEND_CARD
import com.polaris.live.im.sfs.LIVE_SEND_GIFT
import com.polaris.live.im.sfs.bean.live.AbstractCallMessage
import com.polaris.live.im.sfs.bean.live.SendLiveMessage
import com.polaris.live.im.sfs.core.bean.DataArgument
import com.polaris.live.im.sfs.processor.impl.CallEventMessageProcessor
import com.polaris.live.im.utils.PublicMessageUtils
import com.polaris.live.utils.LifecycleListenerDispatcher
import com.polaris.live.utils.MapLifecycleListener
import com.polaris.live.utils.MapLifecycleListenerDispatcher
import kotlinx.coroutines.withContext
import java.util.concurrent.ConcurrentHashMap
import kotlin.reflect.KClass

/**
 * ImCallManager
 *
 * @author Created by lucas on 2023/11/11 16:01
 */
object ImCallManager {

    //公屏的消息类型
    val publicTypes = setOf(
        LIVE_PUBLIC_SCREEN,
        LIVE_PROMPT,
        LIVE_SEND_GIFT,
        LIVE_INVITE_FOLLOW,
        LIVE_INVITE_GIFT,
        LIVE_NEW_FOLLOWER,
        LIVE_RECOMMEND_CARD
    )

    private val callMessageDispatcher =
        MapLifecycleListenerDispatcher<CallCommandWrapper<out AbstractCallMessage>>("callMessageDispatcher")
    private val callPublicMessageDispatcher =
        LifecycleListenerDispatcher<LiveWrapper<OnLivePublicMessageListener>>("callPublicMessageDispatcher")

    /**
     * 存储延迟的消息
     */
    private val delayMessages = ConcurrentHashMap<String, MutableSet<DataArgument>>()

    /**
     * 监听消息，需要先加入到[CallEventMessageProcessor.commands]中
     */
    fun observe(
        owner: LifecycleOwner,
        observerListener: OnLiveObserveListener,
        build: CallMessageBuilder.() -> Unit
    ) {
        build(CallMessageBuilder(owner, observerListener))
    }

    class CallMessageBuilder(
        private val owner: LifecycleOwner,
        private val observerListener: OnLiveObserveListener
    ) {

        inline fun <reified T : AbstractCallMessage> withMessage(
            command: String,
            l: OnCallMessageListener<T>
        ) {
            withMessage(command, T::class, l)
        }

        inline fun <reified T : AbstractCallMessage> withMessage(
            vararg commands: String,
            l: OnCallMessageListener<T>
        ) {
            commands.forEach {
                withMessage(it, T::class, l)
            }
        }

        fun <T : AbstractCallMessage> withMessage(
            command: String,
            type: KClass<T>,
            l: OnCallMessageListener<T>
        ) {
            observe(owner, command, type, observerListener, l)
        }
    }

    /**
     * 监听消息，需要先加入到[CallEventMessageProcessor.commands]中
     */
    inline fun <reified T : AbstractCallMessage> observe(
        owner: LifecycleOwner,
        command: String,
        observerListener: OnLiveObserveListener,
        l: OnCallMessageListener<T>
    ) {
        observe(owner, command, T::class, observerListener, l)
    }

    /**
     * 监听消息，需要先加入到[CallEventMessageProcessor.commands]中
     */
    fun <T : AbstractCallMessage> observe(
        owner: LifecycleOwner,
        command: String,
        type: KClass<T>,
        observerListener: OnLiveObserveListener,
        l: OnCallMessageListener<T>
    ) {
        if (command !in CallEventMessageProcessor.commands) {
            throw IllegalArgumentException("The command to be listened to must be registered in the CallEventMessageProcessor first")
        }

        val wrapper = CallCommandWrapper(
            command,
            observerListener,
            l,
            type
        )

        callMessageDispatcher.observe(owner, wrapper).ifThen {
            //如果有延迟的消息，就下发
            delayMessages[command]?.removeIfBy24 {
                val data = try {
                    it.getData(type)
                } catch (_: Throwable) {
                    null
                } ?: return@removeIfBy24 true

                if (wrapper.isAvailable(data)) {
                    l.onMessage(data)
                    true
                } else {
                    false
                }
            }
        }
    }

    /**
     * 添加公屏消息监听，通过[observerListener]过滤
     */
    fun observePublicMessage(
        owner: LifecycleOwner,
        observerListener: OnLiveObserveListener,
        l: OnLivePublicMessageListener
    ) {
        callPublicMessageDispatcher.observe(owner, LiveWrapper(observerListener, l))
    }

    /**
     * 消息下发
     *
     * @param command 命令
     * @param argument 参数
     * @param delayEvent 是否为延迟事件，如果是延迟事件，当前没有被消费时将会缓存起来，直到有人监听
     */
    internal suspend fun onMessage(command: String, argument: DataArgument, delayEvent: Boolean = false) {
        val listeners = callMessageDispatcher.getListener(command)
        var consumed = false

        if (listeners.isNotEmpty()) {
            listeners.forEach {
                val data = try {
                    argument.getData(it.type)
                } catch (_: Throwable) {
                    null
                }

                if (data != null) {
                    withContext(CoroutineScopeManager.mainDispatcher) {
                        if (it.isAvailable(data)) {
                            (it.value as OnCallMessageListener<AbstractCallMessage>).onMessage(data)
                            consumed = true
                        }
                    }
                }
            }
        }

        if (!consumed && delayEvent) {
            getMessageSet(command).add(argument)
        }
    }

    private fun <T : AbstractCallMessage> CallCommandWrapper<T>.isAvailable(data: AbstractCallMessage): Boolean {
        val callId = observerListener.onObserveId()

        return callId == OnLiveObserveListener.ANY_ID || (callId != null && callId == data.callId)
    }

    private fun getMessageSet(command: String): MutableSet<DataArgument> {
        return delayMessages.computeIfAbsentBy21(command) {
            StructUtils.newSaleSet()
        }
    }

    internal suspend fun onPublicMessage(liveMessage: SendLiveMessage) {
        withContext(CoroutineScopeManager.mainDispatcher) {
            callPublicMessageDispatcher.listeners.forEach {
                val callId = it.observerListener.onObserveId()
                if (callId != null && liveMessage.businessId == callId) {
                    it.value.onMessage(liveMessage)
                    if (liveMessage.type in CallEventMessageProcessor.publicMessages) {
                        PublicMessageUtils.onPublicMessage(liveMessage)
                    }
                }
            }
        }
    }
}

fun interface OnCallMessageListener<T : AbstractCallMessage> : OnLiveMessageListener<T>

data class CallCommandWrapper<T : AbstractCallMessage>(
    override val key: String,
    val observerListener: OnLiveObserveListener,
    val value: OnCallMessageListener<T>,
    val type: KClass<T>,
) : MapLifecycleListener()